/*
 * Copyright (c) 2006-2021, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2019-09-17     tyustli   first version
 */

#include <rtconfig.h> 
#ifdef LCD_USING_TFT_ST7789

#include <rtthread.h>
#include <rtdevice.h>
#include <board.h>
#include <drv_spi.h>
#include <drv_gpio.h>

#define LCD_W_OFFSET 0 // 水平偏移量

 #define LCD_DC_PIN IOC_PAD_PF00  // 添加DC引脚定义
 #define LCD_RST_PIN IOC_PAD_PF01  // 复位引脚
 #define LCD_BL_PIN IOC_PAD_PF02   // 背光控制引脚
//  #define BOARD_SPI_CS_PIN IOC_PAD_PB24   // CS引脚PB24
 struct rt_spi_device *lcd_dev;

 static void lcd_write_dc(int value) {
     rt_pin_write(LCD_DC_PIN, value);
 }

 static void cs_pin_control(int cs)
{
    board_write_spi_cs(BOARD_SPI_CS_PIN, cs); 
}


 static int rt_spi_lcd_init(void)
 {
     rt_pin_mode(LCD_DC_PIN, PIN_MODE_OUTPUT);
     rt_pin_mode(LCD_RST_PIN, PIN_MODE_OUTPUT);
     rt_pin_mode(LCD_BL_PIN, PIN_MODE_OUTPUT);
     
     // 复位序列
     rt_pin_write(LCD_RST_PIN, 0);  // 拉低复位
     rt_thread_mdelay(10);         // 保持100ms
     rt_pin_write(LCD_RST_PIN, 1);  // 释放复位
     rt_thread_mdelay(10);         // 等待复位完成
     
     // 初始化背光(默认打开)
     rt_pin_write(LCD_BL_PIN, 1);

    
     lcd_dev=(struct rt_spi_device *)rt_malloc(sizeof(struct rt_spi_device));    
     struct rt_spi_configuration cfg = {
        .mode = RT_SPI_MODE_0 | RT_SPI_MSB,
        .max_hz = 50 * 1000 * 1000,  // 50MHz, 实际测试20MHz
        .data_width = 8,
    };
     
     /* 挂载SPI设备到总线 */
     RT_ASSERT(rt_spi_bus_attach_device_cspin(lcd_dev, "spi20", "spi2", BOARD_SPI_CS_PIN, cs_pin_control)==RT_EOK);
     
     /* 配置SPI设备参数*/
     RT_ASSERT(rt_spi_configure(lcd_dev, &cfg)==RT_EOK);
 
     return RT_EOK;
 }
 
//  INIT_APP_EXPORT(rt_spi_lcd_init);
 

 static int rt_spi_send_msg_test(void)
 {
    lcd_write_dc(0);
    rt_spi_send(lcd_dev, (uint8_t [14]){0x11, 0x22, 0x33, 0x44, 0x55},15);

    lcd_write_dc(1);
    rt_spi_send(lcd_dev, (uint8_t [14]){0x11, 0x22, 0x33, 0x44, 0x55},18);
 
    lcd_write_dc(0);
    rt_spi_send(lcd_dev, (uint8_t [14]){0x11, 0x22, 0x33, 0x44, 0x55},15);

    lcd_write_dc(1);
    rt_spi_send(lcd_dev, (uint8_t [14]){0x11, 0x22, 0x33, 0x44, 0x55},16);  
      
     return RT_EOK;
 }
 
 MSH_CMD_EXPORT(rt_spi_send_msg_test, rt_spi_send_msg_test!)
 
 
 void write_command(uint8_t cmd)
 {
    /* SPI发送命令，DC线拉低 */
    lcd_write_dc(0);  // DC=0表示命令
    rt_spi_send(lcd_dev, &cmd, 1);
 }
 
 void write_command_data(uint8_t cmd, uint8_t *pdata, uint32_t length)
 {
    /* 先发送命令，再发送数据 */
    lcd_write_dc(0);  // DC=0表示命令
    rt_spi_send(lcd_dev, &cmd, 1);
    
    lcd_write_dc(1);  // DC=1表示数据
    rt_spi_send(lcd_dev, pdata, length);
 }
 
 void write_command_pixels(uint8_t cmd, uint8_t *pdata, uint32_t length)
 {
    /* 发送命令后发送大量像素数据 */
    lcd_write_dc(0);  // DC=0表示命令
    rt_spi_send(lcd_dev, &cmd, 1);
    
    lcd_write_dc(1);  // DC=1表示数据
    rt_spi_send(lcd_dev, pdata, length); 
 }
 
 
 #define MAX_CMD_LEN 32
 static uint8_t lcd_init_cmds[][MAX_CMD_LEN] = {
    {0x3A, 1, 0x05},       //65k mode
    {0xC5, 1, 0x1A},         //VCOM
    {0x36, 1, 0x00},                // 屏幕显示方向设置
    //-------------ST7789V Frame rate setting-----------//
    {0xb2, 5, 0x05, 0x05, 0x00, 0x33, 0x33},                  //Porch Setting
    {0xb7, 1, 0x05},          //Gate Control //12.2v   -10.43v
    //--------------ST7789V Power setting---------------//
    {0xBB, 1, 0x3F},     //VCOM
    {0xC0, 1, 0x2c}, //Power control
    {0xC2, 1, 0x01},      //VDV and VRH Command Enable
    {0xC3, 1, 0x0F},          //VRH Set //4.3+( vcom+vcom offset+vdv)
    {0xC4, 1, 0x20},          //VDV Set//0v
    {0xC6, 1, 0X01},              //Frame Rate Control in Normal Mode //111Hz
    {0xd0, 2, 0xa4, 0xa1},                 //Power Control 1
    {0xE8, 1, 0x03},              //Power Control 1
    {0xE9, 3, 0x09, 0x09, 0x08},                    //Equalize time control
    //---------------ST7789V gamma setting-------------//
    {0xE0, 14, 0xD0, 0x05, 0x09, 0x09, 0x08, 0x14, 0x28, 0x33, 0x3F, 0x07, 0x13, 0x14, 0x28, 0x30},                                       //Set Gamma
    {0XE1, 14, 0xD0, 0x05, 0x09, 0x09, 0x08, 0x03, 0x24, 0x32, 0x32, 0x3B, 0x14, 0x13, 0x28, 0x2F},                                       //Set Gamma
    // {0x21, 0 },        //反显

    {0x2A, 4, 0x00, 0x00, 0x00, 0xef},  //w=240-1
    {0x2B, 4, 0x00, 0x00, 0x01, 0x3f},  //h=320-1





    
    {0x11, 0},
    {0x0, 1, 120},  /*这条伪指令表示延时120ms*/
    {0x29, 0}, 
     
     {0x36, 1, 0x60},   //0x20=旋转90度，0x00=不旋转
 };
 
 static int lcd_init()
 {
    rt_spi_lcd_init();
    clock_set_source_divider(clock_spi2, clk_src_pll1_clk1, 5U);
    rt_kprintf("spi2 freq: %d\n", clock_get_frequency(clock_spi2));
     for (int i = 0; i < sizeof(lcd_init_cmds) / sizeof(lcd_init_cmds[0]); i++) {
         if (lcd_init_cmds[i][0] == 0x0) {
             rt_thread_mdelay(lcd_init_cmds[i][2]);
         }
         else if (lcd_init_cmds[i][1] == 0) {
             write_command(lcd_init_cmds[i][0]);
         }
         else {
             write_command_data(lcd_init_cmds[i][0], &lcd_init_cmds[i][2], lcd_init_cmds[i][1]);
         }
     }
     clock_set_source_divider(clock_spi2, clk_src_pll1_clk1, 2U); /*频率设置最小为1, fpll=400MHz/div=200MHz*/
     rt_kprintf("spi2 freq: %d\n", clock_get_frequency(clock_spi2));
 }
 
 INIT_ENV_EXPORT(lcd_init); /*不能用INIT_APP_EXPORT, 可能会hardfault. 因为与lvgl的初始化存在先后问题.*/
// MSH_CMD_EXPORT(lcd_init, lcd_init!)
 
 void lcd_fill_pixels(uint32_t x0, uint32_t y0, uint32_t x1, uint32_t y1, uint8_t *pcolor)
 {
    x0+=LCD_W_OFFSET;
    x1+=LCD_W_OFFSET;

    uint8_t xwin_list[]={(x0>>8)&0xff, x0&0xff, (x1>>8)&0xff, x1&0xff};
    uint8_t ywin_list[]={(y0>>8)&0xff, y0&0xff, (y1>>8)&0xff, y1&0xff};
    write_command_data(0x2a, xwin_list, 4);
    write_command_data(0x2b, ywin_list, 4);

    uint32_t length=2*(x1-x0+1)*(y1-y0+1);
    // SCB_CleanDCache_by_Addr((uint32_t *)pcolor, length); /*important! drv_spi.c中自带的不行!*/
    write_command_pixels(0x2c, (uint8_t *)pcolor, length);
 }
 
 void lcd_fill_block(uint32_t x0, uint32_t y0, uint32_t x1, uint32_t y1, uint32_t color)
 {
    x0+=LCD_W_OFFSET;
    x1+=LCD_W_OFFSET;
    uint8_t xwin_list[]={(x0>>8)&0xff, x0&0xff, (x1>>8)&0xff, x1&0xff};
    uint8_t ywin_list[]={(y0>>8)&0xff, y0&0xff, (y1>>8)&0xff, y1&0xff};
    write_command_data(0x2a, xwin_list, 4);
    write_command_data(0x2b, ywin_list, 4);

    uint32_t length=2*(x1-x0+1)*(y1-y0+1);
    uint8_t *pcolor=rt_malloc(length);
    if(pcolor==RT_NULL){
        rt_kprintf("malloc failed!\n");
        return; 
    }
    for(int i=0;i<length;i+=2){
        pcolor[i]=color>>8;
        pcolor[i+1]=color&0xff;
    }
    write_command_pixels(0x2c, (uint8_t *)pcolor, length);
    rt_free(pcolor);
 }
 
 void lcd_clear(uint32_t color)
 {
     lcd_fill_block(0, 0, LCD_W/4 - 1, LCD_H/4 - 1, color);
 }
 
 static void lcd_clear_test()
 {
     lcd_clear(0xf800); rt_thread_mdelay(500);
     lcd_clear(0x07e0); rt_thread_mdelay(500);
     lcd_clear(0x001f); rt_thread_mdelay(500);   
 }
 
 MSH_CMD_EXPORT(lcd_clear_test, lcd_clear_test!)
 
 #endif